

package com.lry.rpc.starter.extend;


import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import com.lry.rpc.starter.context.ConfigurationBeanBinder;
import com.lry.rpc.starter.context.ConfigurationBeanCustomizer;
import com.lry.rpc.starter.context.DefaultConfigurationBeanBinder;
import com.lry.rpc.starter.context.DefaultConfigurationBeanCustomizer;
import com.lry.rpc.starter.util.WrapperUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;

public class ConfigurationBeanBindingPostProcessor implements BeanPostProcessor, BeanFactoryAware, PriorityOrdered {
    public static final String BEAN_NAME = "configurationBeanBindingPostProcessor";
    static final String CONFIGURATION_PROPERTIES_ATTRIBUTE_NAME = "configurationProperties";
    static final String IGNORE_UNKNOWN_FIELDS_ATTRIBUTE_NAME = "ignoreUnknownFields";
    static final String IGNORE_INVALID_FIELDS_ATTRIBUTE_NAME = "ignoreInvalidFields";
    private final Log log = LogFactory.getLog(this.getClass());
    private ConfigurableListableBeanFactory beanFactory = null;
    private ConfigurationBeanBinder configurationBeanBinder = null;
    private List<ConfigurationBeanCustomizer> configurationBeanCustomizers = null;
    private int order = 2147483647;

    public ConfigurationBeanBindingPostProcessor() {
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        BeanDefinition beanDefinition = this.getNullableBeanDefinition(beanName);
        if (this.isConfigurationBean(bean, beanDefinition)) {
            this.bindConfigurationBean(bean, beanDefinition);
            this.customize(beanName, bean);
        }

        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public ConfigurationBeanBinder getConfigurationBeanBinder() {
        if (this.configurationBeanBinder == null) {
            this.initConfigurationBeanBinder();
        }

        return this.configurationBeanBinder;
    }

    public void setConfigurationBeanBinder(ConfigurationBeanBinder configurationBeanBinder) {
        this.configurationBeanBinder = configurationBeanBinder;
    }

    public List<ConfigurationBeanCustomizer> getConfigurationBeanCustomizers() {
        if (this.configurationBeanCustomizers == null) {
            this.initBindConfigurationBeanCustomizers();
        }

        return this.configurationBeanCustomizers;
    }

    private void initBindConfigurationBeanCustomizers() {
        Collection<ConfigurationBeanCustomizer> customizers = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, ConfigurationBeanCustomizer.class).values();
        this.setConfigurationBeanCustomizers(customizers);
    }

    private void customize(String beanName, Object configurationBean) {
        for (ConfigurationBeanCustomizer customizer : this.getConfigurationBeanCustomizers()) {
            customizer.customize(beanName, configurationBean);
        }
//        ConfigurationBeanCustomizer customizer = new DefaultConfigurationBeanCustomizer();
//        customizer.customize(beanName, configurationBean);
    }


    public void setConfigurationBeanCustomizers(Collection<ConfigurationBeanCustomizer> configurationBeanCustomizers) {
        List<ConfigurationBeanCustomizer> customizers = new ArrayList(configurationBeanCustomizers);
        AnnotationAwareOrderComparator.sort(customizers);
        this.configurationBeanCustomizers = Collections.unmodifiableList(customizers);
    }

    private BeanDefinition getNullableBeanDefinition(String beanName) {
        return this.beanFactory.containsBeanDefinition(beanName) ? this.beanFactory.getBeanDefinition(beanName) : null;
    }

    private boolean isConfigurationBean(Object bean, BeanDefinition beanDefinition) {
        return beanDefinition != null && ConfigurationBeanBindingRegistrar.ENABLE_CONFIGURATION_BINDING_CLASS.equals(beanDefinition.getSource()) && ObjectUtils.nullSafeEquals(this.getBeanClassName(bean), beanDefinition.getBeanClassName());
    }

    private String getBeanClassName(Object bean) {
        return ClassUtils.getUserClass(bean.getClass()).getName();
    }

    private void bindConfigurationBean(Object configurationBean, BeanDefinition beanDefinition) {
        Map<String, Object> configurationProperties = getConfigurationProperties(beanDefinition);
        boolean ignoreUnknownFields = getIgnoreUnknownFields(beanDefinition);
        boolean ignoreInvalidFields = getIgnoreInvalidFields(beanDefinition);
        this.getConfigurationBeanBinder().bind(configurationProperties, ignoreUnknownFields, ignoreInvalidFields, configurationBean);
        if (this.log.isInfoEnabled()) {
            this.log.info("The configuration bean [" + configurationBean + "] have been binding by the configuration properties [" + configurationProperties + "]");
        }

    }

    private void initConfigurationBeanBinder() {
        if (this.configurationBeanBinder == null) {
            try {
                this.configurationBeanBinder = this.beanFactory.getBean(ConfigurationBeanBinder.class);
            } catch (BeansException var2) {
                if (this.log.isInfoEnabled()) {
                    this.log.info("configurationBeanBinder Bean can't be found in ApplicationContext.");
                }

                this.configurationBeanBinder = this.defaultConfigurationBeanBinder();
            }
        }

    }


    private ConfigurationBeanBinder defaultConfigurationBeanBinder() {
        return new DefaultConfigurationBeanBinder();
    }

    static void initBeanMetadataAttributes(AbstractBeanDefinition beanDefinition, Map<String, Object> configurationProperties, boolean ignoreUnknownFields, boolean ignoreInvalidFields) {
        beanDefinition.setAttribute("configurationProperties", configurationProperties);
        beanDefinition.setAttribute("ignoreUnknownFields", ignoreUnknownFields);
        beanDefinition.setAttribute("ignoreInvalidFields", ignoreInvalidFields);
    }

    private static <T> T getAttribute(BeanDefinition beanDefinition, String attributeName) {
        return (T) beanDefinition.getAttribute(attributeName);
    }

    private static Map<String, Object> getConfigurationProperties(BeanDefinition beanDefinition) {
        return (Map)getAttribute(beanDefinition, "configurationProperties");
    }

    private static boolean getIgnoreUnknownFields(BeanDefinition beanDefinition) {
        return (Boolean)getAttribute(beanDefinition, "ignoreUnknownFields");
    }

    private static boolean getIgnoreInvalidFields(BeanDefinition beanDefinition) {
        return (Boolean)getAttribute(beanDefinition, "ignoreInvalidFields");
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = WrapperUtils.unwrap(beanFactory);
    }

    @Override
    public int getOrder() {
        return this.order;
    }
}
